using System;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Web;
using System.Collections;

namespace XTCast.Radio {
	
	/// <summary>A "consumer" Thread that sends MP3 audio to a remote client
	/// by writing data from Buffers to Sockets.
	/// There is one of these per active client connection to the server.
	/// </summary>
	
	public class Transmitter:IHttpHandler,System.Web.SessionState.IRequiresSessionState {
		
		/// <summary>Creates a Transmitter, but doesn't start it running yet.</summary>
		/// <param name="dj"> The Station's DJ
		/// </param>
		/// <param name="s">  The Socket to write to
		/// </param>
		/// <param name="udpPort">The Udp Port the listener wants title updates 0=not wanted 
		/// </param>
		public Transmitter() {
		}

		bool popUp = true;
		public bool PopUp {
			get {
				if (popUp) {
					popUp=false;
					return true;
				} 
				else return false;
			}
			set {
				popUp = value;
			}
		}

		Track _track=null;
		void writeTitle(System.IO.Stream outs, Track track) {
			if (_track==track || track==null ) {
				outs.WriteByte(0);
				return;
			}
			_track=track;

			string str = "StreamTitle='" + track.ToString() + "';StreamUrl='" + station.StationConfig.Identity.GetHomePage(Request.Url,PopUp) +  "';";

			Encoding iso8859 = Encoding.GetEncoding("ISO-8859-1");
			byte[] title = iso8859.GetBytes(str);
			byte lenbyte=(byte)(((title.Length+1)/16)+1);
			byte[] buf = new byte[(lenbyte*16)+1];
			Array.Clear(buf,0,buf.Length);
			buf[0] = lenbyte;
			Array.Copy(title,0,buf,1,title.Length);
			outs.Write(buf,0,buf.Length);
		}
				
		//		/// <summary>Writes the contents of a Buffer to the Socket. </summary>
		//		private long SendBuffer(Buffer buf, int interval, ref int sendBefore) 
		//		{
		//		}
		//
		public bool Paused {
			get {
				int pos = iterator.Position;
				return (pos < 0 || pos>=bm.Count-1);
			}
		}

		private void TransmitBuffer(Stream fout ) {
			Buffer buf = iterator.NextBuffer();
			if (buf == null) return;
			int millis;
			if (interval<=0)
				millis = buf.WriteTo(fout); // will block for quite a while!
			else
				millis = buf.WriteTo(fout,interval,ref sendBefore,new Buffer.WriteTitleDelegate(writeTitle)); // will block for quite a while!
			fout.Flush(); 
			Log.Verbose(this, "Finished sending " + buf);
		}
		
		// Current Buffer Transmitting
		public BufferIterator iterator;
		private BufferManager bm;
		private Station station;
		private int fUdpPort;
		private int interval = -1, sendBefore;
		public string Name;
		private System.Web.HttpRequest Request;
		private System.Web.HttpResponse Response;

		static int disconnectIndex;
		static int disconnectCookie;
		static DateTime disconnectTime;

		static void Disconnect(int index, int cookie) {
			if (index<=0 || cookie<=0) return;
			disconnectIndex=index;
			disconnectCookie=cookie;
			disconnectTime = DateTime.Now;
		}

		static int DisconnectIndex(int cookie) {
			if (disconnectCookie!=cookie) return -1;
			if (DateTime.Now-disconnectTime>TimeSpan.FromSeconds(5))  return -1;
			return disconnectIndex;
		}
	
		public void ProcessRequest(System.Web.HttpContext context) {
			try {
				Request=context.Request;
				Response=context.Response;

				station = (Station)context.Application.Get("Station"); 
				if (station==null) {
					context.Response.Write("Current station is not loaded");
					return;
				}
				this.bm = station.BufferManager;
				IdentityConfig i = station.StationConfig.Identity;
				
				String reqMetadata=context.Request.Headers.Get("Icy-Metadata");
				if (reqMetadata=="1") {
					interval=Buffer.sSize;
					sendBefore=Buffer.sSize;
				}

				int cookie=-1;
				int index=-1;

				if (context.Request.Params["Cookie"]!=null && context.Request.Params["Index"]!=null) {
					cookie=int.Parse(context.Request.Params["Cookie"]);
					index=int.Parse(context.Request.Params["Index"]);
					int prevIndex=DisconnectIndex(cookie);
					if (prevIndex>0 && index!=prevIndex) {
						if (index==prevIndex-1 || (prevIndex==1 && index==10))
							station.PlaylistFilter.PlayPrevTrack();
						else
							station.PlaylistFilter.PlayNextTrack();
					}
				}

				String xUdpPort=context.Request.Headers.Get("x-audiocast-udpport");
				if (xUdpPort!=null)
					fUdpPort = int.Parse(xUdpPort);
				else
					fUdpPort = 0;

				//context.Response.ClearHeaders();
				context.Response.AppendHeader("Server", Radio.NameAndVersion);
				context.Response.AppendHeader("Content-Type","audio/mpeg");

				if (i.Name != null) {
					context.Response.AppendHeader("icy-name",i.Name);
					context.Response.AppendHeader("ice-name",i.Name);
				}
				if (i.Genre != null) {
					context.Response.AppendHeader("icy-genre",i.Genre);
					context.Response.AppendHeader("ice-genre",i.Genre);
				}
				if (interval >0) {
					context.Response.Status="200 ICY 200 OK";
					context.Response.AppendHeader("icy-metaint",interval.ToString());
				}
				string url = i.GetHomePage(context.Request.Url,this.PopUp);
				if (url!=null) {
					context.Response.AppendHeader("icy-url" , url);
					context.Response.AppendHeader("ice-url" , url);
				}

				iterator = bm.GetBufferIterator();

				Response.Buffer=false;
				
				station.TransmitterStarted(this);
				Stream fout = Response.OutputStream;
				try {
					while ( station.Run && context.Response.IsClientConnected)
						TransmitBuffer(fout);
				}
				finally {
					Disconnect(index,cookie);
					station.TransmitterStopped(this);
				}
			} 
			catch (Exception ex) {
				Log.Error(ex);
			}
		}

		public override string ToString() {
			return String.Format("T{0,14:G}", Request.UserHostAddress);
		}

	
		public bool IsReusable {
			get {
				return false;
			}
		}
	}
}